package org.lightpersist.hibernate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.persistence.Entity;

import org.aopalliance.intercept.MethodInterceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.lightcommons.util.Assert;
import org.lightcommons.util.EncodingProperties;
import org.lightcommons.util.PackageUtils;
import org.lightpersist.dao.DaoTemplate;
import org.lightpersist.dao.hibernate.HibernateTemplate;
import org.lightpersist.util.GuiceUtils;

import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;

/**
 * HibernateModule for guice.
 * Create On: 2008-12-25
 * @author GL
 *
 */
public class HibernateModule implements Module{
	private static Injector injector;
	private final Configuration configuration;
	private String[] packages;
	private List<MethodInterceptor> interceptorList=new ArrayList<MethodInterceptor>();
	
	public static void setInjector(Injector injector) {
		HibernateModule.injector = injector;
	}
	
	static Injector getInjector() {
		return injector;
	}
	
	public HibernateModule(Configuration configuration,String... packages) {
		Assert.notEmpty(packages, "targetPackageName can't be empty!");
		this.configuration = configuration;
		this.packages=packages;
	}

	public HibernateModule(Collection<Class<?>> annotatedClasses,Properties properties,String... packages){
		Assert.notEmpty(packages, "targetPackageName can't be empty!");
		AnnotationConfiguration cfg = new AnnotationConfiguration();
		for(Class cls:annotatedClasses){
			if(cls.getAnnotation(Entity.class)!=null)
				cfg.addAnnotatedClass(cls);
		}
		if(properties!=null && !properties.isEmpty())
			cfg.addProperties(properties);
		this.configuration = cfg;
		this.packages = packages;
	}
	
	public HibernateModule(String modelPackage,String properties,String... packages){
		this(PackageUtils.getClasses(modelPackage),EncodingProperties.loadFromClassPath(properties, "UTF-8"),packages);
	}
	
	public HibernateModule addMethodInterceptor(MethodInterceptor interceptor){
		this.interceptorList.add(interceptor);
		return this;
	}
	
	public void setPackages(String[] packages) {
		this.packages = packages;
	}
	
	public void configure(Binder binder) {
		//bind common classes
		binder.bind(SessionFactory.class).toProvider(SessionFactoryProvider.class);
		binder.bind(Session.class).toProvider(ThreadLocalSessionProvider.class);
		binder.bind(Configuration.class).toInstance(configuration);
		binder.bind(DaoTemplate.class).to(HibernateTemplate.class);
		
		//prepare interceptors
		interceptorList.add(0, new AutoImplementInterceptor());
		interceptorList.add(0, new HibernateLocalTxnInterceptor());
		MethodInterceptor[] interceptors = new MethodInterceptor[0];
		interceptors = interceptorList.toArray(interceptors);
		
		//binding interceptors
		Matcher<? super Class> typeMatcher = GuiceUtils.inSubpackages(packages);
		binder.bindInterceptor(typeMatcher, Matchers.any() , interceptors);
		Set<Class<?>> classes = new HashSet<Class<?>>();
		for(String packageName:packages){
			classes.addAll(PackageUtils.getClasses(packageName));
		}
		GuiceUtils.bindDynamicInterceptor(binder, classes, typeMatcher, Matchers.any(), interceptors);
	}
	
}
